﻿using System.Collections.Generic;
using System.IO;

namespace System
{
	public delegate bool LogicalAction<T>(T item);
	public delegate void GraphPointEvent<TP, TE>(GraphPoint<TP, TE> P);

	public partial class GraphPoint<TP, TE>
	{
		public int ID;
		public TP Content;
		public object Tag;
		public List<GraphEdge<TE, TP>> Edges = new List<GraphEdge<TE, TP>>();
		public List<GraphEdge<TE, TP>> FindEdges(Predicate<GraphEdge<TE, TP>> action)
		{
			return Edges.FindAll(action);
			/*
			List<GraphEdge<TE, TP>> Res = new List<GraphEdge<TE, TP>>();
			foreach (var E in Edges)
				if (action(E))
					Res.Add(E);
			return Res;
			*/
		}
		/*
		public GraphEdge<TE, TP> Bind(GraphPoint<TP, TE> p, TE edgecontent)
		{
			var X = FindEdges(x => x.FarEnd == p);
			if (X.Count == 0)
			{
				var newedge = new GraphEdge<TE, TP>(this, p, edgecontent);
				Edges.Add(newedge);
				return newedge;
			}
			else
				X[0].Content = edgecontent;
			return X[0];
		}
		public void _Bind(GraphEdge<TE, TP> edge)
		{
			if (edge.ThisEnd != this)
				throw new Exception("Start point mismatch");
			var X = FindEdges(x => x.FarEnd == edge.FarEnd);
			if (X.Count > 0)
				throw new Exception("Edge duplication error");
			Edges.Add(edge);
		}
		*/
		public void UnBind(GraphPoint<TP, TE> adj)
		{
			var X = FindEdges(x => x.FarEnd == adj);
			if (X.Count > 0)
			{
				/**Adjacents.Remove(adj);*/
				Edges.Remove(X[0]);
				adj.UnBind(this);
				UnBind(adj);
			}
		}
		public GraphPoint(TP obj)
		{
			Content = obj;
		}

		public GraphPoint(String S)
		{
			Content = (TP)Activator.CreateInstance(typeof(TP), S);
		}
		public static Graph<TP, TE> FromVoronoi(Delaunay D)
		{
			Graph<TP, TE> Res = new Graph<TP, TE>();
			foreach (var P in D.Points)
			{
				Res.Points.Add(new GraphPoint<TP, TE>(default(TP)));
			}
			return Res;
		}
	}
	public class GraphEdge<TE, TP>
	{
		public TE Content;
		public GraphPoint<TP, TE> ThisEnd;
		public GraphPoint<TP, TE> FarEnd;
		public GraphEdge(GraphPoint<TP, TE> p1, GraphPoint<TP, TE> p2, TE edgecontent)
		{
			ThisEnd = p1;
			FarEnd = p2;
			Content = edgecontent;
		}
		public virtual void Calculate()
		{
		}

		public virtual void Save(TextWriter W)
		{
			W.WriteLine(ThisEnd.ID + "-" + FarEnd.ID);
			if (GetType().GetMethod("Save").DeclaringType == typeof(GraphEdge<TE, TP>))
				W.WriteLine("<<Point save not implemented>>");
		}
	}

	public class Graph<TP, TE>
	{
		public List<GraphPoint<TP, TE>> Points;
		public GraphPoint<TP, TE> this[TP item]
		{
			get
			{
				for (int i = 0; i < Points.Count; i++)
					if (Points[i].Content.Equals(item))
						return Points[i];
				return null;
			}
		}
		public List<GraphEdge<TE, TP>> Edges;
		public Graph()
		{
			Points = new List<GraphPoint<TP, TE>>();
			Edges = new List<GraphEdge<TE, TP>>();
		}

		public GraphPoint<TP, TE> AddPoint(TP obj)
		{
			GraphPoint<TP, TE> Res = new GraphPoint<TP, TE>(obj);
			Res.ID = Points.Count;
			Points.Add(Res);
			return Res;
		}
		public void Add(GraphPoint<TP, TE> node)
		{
			node.ID = Points.Count;
			Points.Add(node);
		}

		public GraphEdge<TE, TP> Bind(GraphPoint<TP, TE> p, GraphPoint<TP, TE> a, TE content)
		{
			var E = Edges.Find(x => (x.ThisEnd == p && x.FarEnd == a));
			if (E != null)
				E.Content = content;
			else
			{
				var e = new GraphEdge<TE, TP>(p, a, content);
				Bind(e);
			}
			return E;
		}
		public void Bind(GraphEdge<TE, TP> edge)
		{
			Edges.Add(edge);
			edge.ThisEnd.Edges.Add(edge);
		}

		public void Delete(GraphPoint<TP, TE> p)
		{
			for (int i = 0; i < Edges.Count; )
			{
				if (Edges[i].ThisEnd == p)
					Edges.RemoveAt(i);
				else if (Edges[i].FarEnd == p)
				{
					Edges[i].ThisEnd.Edges.Remove(Edges[i]);
					Edges.RemoveAt(i);
				}
				else
					i++;
			}
			Points.Remove(p);
		}
		public void Clear()
		{
			foreach (var p in Points)
				p.Content = default(TP);
			Points.Clear();
			GC.Collect();
		}
	}
}